/*
 *
 *   Copyright (C) 2005 by Raymond Huang
 *   plushuang at users.sourceforge.net
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  ---
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU Lesser General Public License in all respects
 *  for all of the code used other than OpenSSL.  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so.  If you
 *  do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */

#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <urlglib/urlglib_util.h>

#ifdef G_OS_WIN32
#include <io.h>
#include <windows.h>
#endif

int  test_file_exist_utf8 (const char* path)
{
	char*  filename_os;
	int    return_val;
	gboolean utf8_fs;

	utf8_fs  = g_get_filename_charsets (NULL);
	if (utf8_fs)
		filename_os = (char*)path;
	else
		filename_os = g_filename_from_utf8 (path, -1, NULL, NULL, NULL);

	return_val = g_file_test (filename_os, G_FILE_TEST_EXISTS);

	if (utf8_fs == FALSE)
		g_free (filename_os);

	return return_val;
}

char* strndup_no_crlf (const char* str, int len)
{
	const char* str_end;
	char* new_string;
	char* cur;

	if (str) {
		if (len == -1)
		    len = strlen (str);
		if (len) {
			str_end = str + len;
			new_string = g_malloc (len + 1);
			for (cur=new_string; str != str_end; str++) {
				if (*str != '\r' && *str != '\n')
					*cur++ = *str;
			}
			*cur = 0;
			return new_string;
		}
	}
	return NULL;
}

void  str_replace_no_crlf (char** dest_pstr, const char* src, int src_len)
{
	char* old_string;

	if (dest_pstr) {
//		if (src && src_len == -1)
//			src_len = strlen (src);
		old_string   = *dest_pstr;
//		*dest_pstr = (src && *src) ? strndup_no_crlf (src, src_len) : NULL;
		*dest_pstr = strndup_no_crlf (src, src_len);
		g_free (old_string);
	}
}

void  str_replace (char** dest_pstr, const char* src, int src_len)
{
	char* old_string;

	if (dest_pstr) {
		if (src && src_len == -1)
			src_len = strlen (src);
		old_string   = *dest_pstr;
		*dest_pstr = (src && *src) ? g_strndup (src, src_len) : NULL;
		g_free (old_string);
	}
}

const char* text_get_line_beg (const char* text, char** line, int* line_len)
{
	*line = NULL;
	*line_len = 0;

	while (1) {
		if (*line == NULL) {
			if (*text!=' ' && *text!='\r' && *text!='\n') {
				*line = (char*)text;
			}
		} else {
			if (*text=='\r' || *text=='\n' || *text == 0) {
				*line_len = (char*)text - *line;
				break;
			}
		}
		if (*text==0)
			break;
		text++;
	}
	return (*text) ? text : NULL;
}

// string functions -------------------------------------------------

int  ug_str_next_line (const char* string, int len, int start_offset)
{
	int   result = start_offset;
	const char* string_end = string + len;

	for (string+=start_offset; string < string_end; string++, result++) {
		if (*string == '\n' && string+1 < string_end) {
			result++;
			return result;
		}
	}

	return 0;
}

int  ug_str_line_len (const char* string, int len, int start_offset)
{
	int   result = 0;
	const char* string_end = string + len;

	for (string+=start_offset; string < string_end; string++, result++) {
		if (*string == '\r' || *string == '\n')
			break;
	}

	return result;
}

int  ug_str_skip_space (const char* string, int len, int start_offset)
{
	int   result = start_offset;
	const char* string_end = string + len;

	for (string+=start_offset; string < string_end; string++, result++) {
		if (*string != ' ')
			break;
	}

	return result;
}

void ug_str_replace (char** dest_address, const char* src, int src_length)
{
	g_free (*dest_address);

	if (src == NULL || src_length == 0)
		*dest_address = NULL;
	else {
		if (src_length == -1)
			*dest_address = g_strdup (src);
		else
			*dest_address = g_strndup (src, src_length);
	}
}

// file & directory functions ---------------------------------------

// This function use complex way to handle directory because some locale encoding doesn't avoid '\\' or '/'.
gboolean ug_make_dirs (const gchar* dir, gint len)
{
	gchar*  path_beg;
	gchar*  path_end;
	gchar*  path_cur;
	gchar*  path_os;
	gboolean utf8_fs;
	gboolean result = TRUE;

//	assert (dir);
	if (len == -1)
		len = strlen (dir);

	path_beg = g_strndup (dir, len);
	path_end = path_beg + len;
	utf8_fs  = g_get_filename_charsets (NULL);
	if (utf8_fs)
		path_os = path_beg;

	// path_cur = path_beg+1;   // avoid problem when directory look like "/foo" or "\\foo".
	for (path_cur = path_beg+1; result, path_cur <= path_end; path_cur++) {
		if (*path_cur != G_DIR_SEPARATOR && *path_cur != 0)
			continue;

		*path_cur = 0;
		if (utf8_fs == FALSE)
			path_os = g_filename_from_utf8 (path_beg, -1, NULL, NULL, NULL);

		if (path_os == NULL || *path_os == 0) {
			// return FALSE if converting fail.
			result = FALSE;
		} else if (g_file_test (path_os, G_FILE_TEST_EXISTS)) {
			if (g_file_test (path_os, G_FILE_TEST_IS_DIR) == FALSE)
				result = FALSE;
		} else if (g_mkdir (path_os, 0750) == -1) {
			result = FALSE;
		}

		if (utf8_fs == FALSE)
			g_free (path_os);
		*path_cur = G_DIR_SEPARATOR;
	}

	g_free (path_beg);
	return result;
}

#ifdef G_OS_WIN32

#ifndef __MINGW32__
int    ug_fseek (FILE* file, gint64 position, int mode)
{
	int fd;
	HANDLE h_file;
	LARGE_INTEGER int64_val;

	switch (mode) {
	case SEEK_SET:
		mode = FILE_BEGIN;
		break;
	case SEEK_CUR:
		mode = FILE_CURRENT;
		break;
	case SEEK_END:
		mode = FILE_END;
		break;
	default:
		return 1;
	};

	fd     = fileno(file);
	h_file = (HANDLE)_get_osfhandle(fd);
	int64_val.QuadPart = position;

	SetFilePointer (h_file, int64_val.LowPart, &int64_val.HighPart, mode);
	if (GetLastError () == NO_ERROR)
		return 0;

	return 1;
}

gint64 ug_ftell (FILE* file)
{
	int fd;
	HANDLE h_file;
	LARGE_INTEGER int64_val;

	fd     = fileno(file);
	h_file = (HANDLE)_get_osfhandle(fd);

	int64_val.LowPart = GetFileSize (h_file, &int64_val.HighPart);

	return int64_val.QuadPart;
}

FILE* ug_fopen (const gchar *filename, const gchar *mode)
{
  if (G_WIN32_HAVE_WIDECHAR_API ())
    {
      wchar_t *wfilename = g_utf8_to_utf16 (filename, -1, NULL, NULL, NULL);
      wchar_t *wmode;
      FILE *retval;
      int save_errno;

      if (wfilename == NULL)
	{
	  errno = EINVAL;
	  return NULL;
	}

      wmode = g_utf8_to_utf16 (mode, -1, NULL, NULL, NULL);

      if (wmode == NULL)
	{
	  g_free (wfilename);
	  errno = EINVAL;
	  return NULL;
	}
	
      retval = _wfopen (wfilename, wmode);
      save_errno = errno;

      g_free (wfilename);
      g_free (wmode);

      errno = save_errno;
      return retval;
    }
  else
    {
      gchar *cp_filename = g_locale_from_utf8 (filename, -1, NULL, NULL, NULL);
      FILE *retval;
      int save_errno;

      if (cp_filename == NULL)
	{
	  errno = EINVAL;
	  return NULL;
	}

      retval = fopen (cp_filename, mode);
      save_errno = errno;

      g_free (cp_filename);

      errno = save_errno;
      return retval;
    }
}
#endif  //  ifndef __MINGW32__

#else  // G_OS_WIN32

FILE* ug_fopen (const gchar *filename, const gchar *mode)
{
	if (g_get_filename_charsets (NULL))
		return g_fopen (filename, mode);
	else {
		gchar *cp_filename = g_filename_from_utf8 (filename, -1, NULL, NULL, NULL);
		FILE *retval;
		int save_errno;

		if (cp_filename == NULL) {
			errno = EINVAL;
			return NULL;
		}

		retval = g_fopen (cp_filename, mode);
		save_errno = errno;

		g_free (cp_filename);

		errno = save_errno;
		return retval;
	}
}

int    ug_unlink (const gchar* filename)
{
	if (g_get_filename_charsets (NULL))
		return g_unlink (filename);
	else {
		gchar *cp_filename = g_filename_from_utf8 (filename, -1, NULL, NULL, NULL);
		int save_errno;
		int retval;

		if (cp_filename == NULL) {
			errno = EINVAL;
			return -1;
		}

		retval = g_unlink (cp_filename);
		save_errno = errno;

		g_free (cp_filename);

		errno = save_errno;
		return retval;
	}
}

int    ug_rename (const gchar* old_filename, const gchar* new_filename)
{
	if (g_get_filename_charsets (NULL))
		return g_rename (old_filename, new_filename);
	else {
		gchar *cp_old_filename = g_filename_from_utf8 (old_filename, -1, NULL, NULL, NULL);
		gchar *cp_new_filename = g_filename_from_utf8 (new_filename, -1, NULL, NULL, NULL);
		int save_errno;
		int retval;

		if (cp_old_filename == NULL || cp_new_filename == NULL) {
			g_free (cp_old_filename);
			g_free (cp_new_filename);
			errno = EINVAL;
			return -1;
		}

		retval = g_rename (cp_old_filename, cp_new_filename);
		save_errno = errno;

		g_free (cp_old_filename);
		g_free (cp_new_filename);

		errno = save_errno;
		return retval;
	}
}

#endif  // End of G_OS_WIN32
